/*
 * Copyright (C) 2017 C-SKY Microsystems Co., Ltd. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <csi_config.h>

#ifdef CONFIG_ETH_ENC28J60

#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifndef CONFIG_KERNEL_NONE
#include <csi_kernel.h>
#endif
#include "csi_eth_enc28j60.h"

#include "pin.h"
#include "soc.h"
#include "drv_spi.h"
#include "drv_gpio.h"
#include "drv_eth.h"
#include "drv_eth_phy.h"
#include "drv_eth_mac.h"

#include <errno.h>

#define ETH_LOG_DBG(format, ...) printf(format,##__VA_ARGS__)

#define NET_HWADDR_LEN  6

static uint8_t  Enc28j60Bank;
static uint16_t NextPacketPtr;

static spi_handle_t g_net_spi_hd = NULL;
static gpio_pin_handle_t   pgpio_pin_handle1;
//static k_sem_handle_t g_sem_spi_tx_hd = NULL;
//static k_sem_handle_t g_sem_spi_rx_hd = NULL;

static eth_mac_priv_t s_eth_instance[CONFIG_ETH_NUM];
static eth_phy_priv_t s_phy_instance[CONFIG_ETH_NUM];
static eth_mac_handle_t mac_handle = (eth_mac_handle_t) &s_eth_instance[0];
static uint8_t g_hw_addr[NET_HWADDR_LEN] = {0};

static uint8_t enc28j60ReadOp(uint8_t op, uint8_t address);
static void enc28j60WriteOp(uint8_t op, uint8_t address, uint8_t data);
static void enc28j60ReadBuffer(const uint16_t len, uint8_t *data);
static void enc28j60WriteBuffer(uint16_t len, uint8_t *data);
static void enc28j60SetBank(uint8_t address);
static uint8_t enc28j60Read(uint8_t address);
static void enc28j60Write(uint8_t address, uint8_t data);
static uint32_t enc28j60PhyWrite(uint8_t phy_addr, uint8_t reg_addr, uint16_t  data);
static uint32_t enc28j60Phyregread(uint8_t phy_addr, uint8_t reg_addr, uint16_t *data);
static void enc28j60Init(const uint8_t *macaddr);
static int enc28j60Reset(int obj);
static int enc28j60ResetInit(int obj);
static uint16_t enc28j60GetRxFreeSpace(void);

extern bool eth_gpio_get_level(void);

void enc28j60_spi_transfer_callback(int32_t idx, spi_event_e event)
{
    if (event == SPI_EVENT_TRANSFER_COMPLETE) {

    } else if (event == SPI_EVENT_TX_COMPLETE) {
        //csi_kernel_sem_post(g_sem_spi_tx_hd);
    } else if (event == SPI_EVENT_RX_COMPLETE) {
        //csi_kernel_sem_post(g_sem_spi_rx_hd);
    }/* else if (event == SPI_EVENT_DATA_LOST) {

        printf("TRANSFER_DATA_LOST\n");
    } */else {
        printf("spi err %d\n", event);
    }
}

void enc28j60_spi_cs_status_change(int status)
{
    csi_gpio_pin_write(pgpio_pin_handle1, status);
}

static int32_t enc28j60_spi_send(spi_handle_t handle, const void *data, uint32_t num)
{
    csi_spi_config_block_mode(handle, 1);
    csi_spi_send(handle, data, num);
    csi_spi_config_block_mode(handle, 0);
    // csi_kernel_sem_wait(g_sem_spi_tx_hd, -1);

    return 0;
}

static int32_t enc28j60_spi_receive(spi_handle_t handle, void *data, uint32_t num)
{
    csi_spi_config_block_mode(handle, 1);
    csi_spi_receive(handle, data, num);
    csi_spi_config_block_mode(handle, 0);
    //csi_kernel_sem_wait(g_sem_spi_rx_hd, -1);

    return 0;
}

/**
 * read ctrl register
 * @param op operation cmd
 * @param register address
 *
 * @return
 *        -register data
 */
static uint8_t enc28j60ReadOp(uint8_t op, uint8_t address)
{
    uint8_t   dat = 0;

    ENC28J60_CSL();

    dat = (op | (address & ADDR_MASK));

    uint8_t rdata[1] = {0};

    enc28j60_spi_send(g_net_spi_hd, &dat, 1);

    enc28j60_spi_receive(g_net_spi_hd, &rdata[0], 1);

    /* do dummy read if needed (for mac and mii, see datasheet page 29) */
    if (address & 0x80) {
        enc28j60_spi_receive(g_net_spi_hd, &rdata[0], 1);
    }

    /* release CS */
    ENC28J60_CSH();
    return rdata[0];

}

/**
 * write ctrl cmd to register
 * @param op operation cmd
 * @param register address
 * @param data the data to be set
 *
 * @return
 *        -NULL
 */
static void enc28j60WriteOp(uint8_t op, uint8_t address, uint8_t data)
{
    char dat = 0;

    ENC28J60_CSL();
    /* issue write command */
    dat = op | (address & ADDR_MASK);

    enc28j60_spi_send(g_net_spi_hd, &dat, 1);

    dat = data;
    enc28j60_spi_send(g_net_spi_hd, &dat, 1);

    ENC28J60_CSH();
}

/**
 * read buffer data
 * @param len the data length waint to read
 * @param data the data buffer
 *
 * @return
 *        -NULL
 */
static void enc28j60ReadBuffer(uint16_t len, uint8_t *data)
{
    char ops_ctr = ENC28J60_READ_BUF_MEM;

    ENC28J60_CSL();
    /* issue read command */

    enc28j60_spi_send(g_net_spi_hd, &ops_ctr, 1);

    enc28j60_spi_receive(g_net_spi_hd, (char *)&data[0], len);

    ENC28J60_CSH();
}

/**
 * write data to buffer
 * @param len the data length waint to write
 * @param data the data buffer pointer
 *
 * @return
 *        -NULL
 */
static void enc28j60WriteBuffer(uint16_t len, uint8_t *data)
{
    char ops_ctr = ENC28J60_WRITE_BUF_MEM;

    ENC28J60_CSL();
    /* issue write command */
    //drv_porting_spi_write(NULL, &ops_ctr, 1);

    enc28j60_spi_send(g_net_spi_hd, &ops_ctr, 1);

    enc28j60_spi_send(g_net_spi_hd, &data[0], len);

    ENC28J60_CSH();
}

/**
 * select the bank to operation
 * @param address the bank address
 *
 * @return
 *        -NULL
 */
static void enc28j60SetBank(uint8_t address)
{
    /* set the bank (if needed) */
    if ((address & BANK_MASK) != Enc28j60Bank) {
        /* set the bank */
        enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, (ECON1_BSEL1 | ECON1_BSEL0));
        enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, (address & BANK_MASK) >> 5);
        Enc28j60Bank = (address & BANK_MASK);
    }
}

/**
 *  read ctrl register data
 * @param address the ctrl register address
 *
 * @return
 *        -ctrl register data
 */
static uint8_t enc28j60Read(uint8_t address)
{
    /* set the bank */
    enc28j60SetBank(address);
    /* do the read */
    return enc28j60ReadOp(ENC28J60_READ_CTRL_REG, address);
}

/**
 *  write data to ctrl register
 * @param address the ctrl register address
 * @param data ctrl register cmd
 *
 * @return
 *        - NULL
 */
static void enc28j60Write(uint8_t address, uint8_t data)
{
    /* set the bank */
    enc28j60SetBank(address);
    /* do the write */
    enc28j60WriteOp(ENC28J60_WRITE_CTRL_REG, address, data);
}

/**
 *  write data to phy register
 * @param address the phy register address
 * @param data phy register cmd
 *
 * @return
 *        - NULL
 */
static uint32_t enc28j60PhyWrite(uint8_t phy_addr, uint8_t reg_addr, uint16_t  data)
{
    int retry = 0;

    /* set the PHY register address */
    enc28j60Write(MIREGADR, phy_addr);
    /* write the PHY data */
    enc28j60Write(MIWRL, data);
    enc28j60Write(MIWRH, data >> 8);

    /* wait until the PHY write completes */
    while (enc28j60Read(MISTAT) & MISTAT_BUSY) {
        retry++;

        if (retry > 0xFFF) {
            printf("write reg err\n");
            return -1;
        }
    }

    return 0;
}

/**
 *  read data from phy register
 * @param address the phy register address
 *
 * @return
 *        -the data of phy register
 */
static uint32_t enc28j60Phyregread(uint8_t phy_addr, uint8_t reg_addr, uint16_t *data)
{
    uint8_t  temp;
    int      retry = 0;

    temp = enc28j60Read(MICMD);
    /*  set the PHY register address */
    enc28j60Write(MIREGADR, phy_addr);

    enc28j60Write(MICMD, temp | MICMD_MIIRD);

    /*  Loop to wait until the PHY register has been read through the MII */
    while ((enc28j60Read(MISTAT) & MISTAT_BUSY)) {
        if (retry++ > 0xFFF) {
            printf("read reg err\n");
            return -1;
        }
    }

    enc28j60Write(MICMD, temp & (~MICMD_MIIRD));   /* clear bit MICMD.MIIRD */

    /*  Obtain results and return */
    *data = enc28j60Read(MIRDL);
    *data |= (enc28j60Read(MIRDH) << 8);

    return *data;
}

/**
 *   init ethernet ctrl register
 * @param address the MAC address
 *
 * @return
 *        - NULL
 */
static void enc28j60Init(const uint8_t *macaddr)
{
    /*  check CLKRDY bit to see if reset is complete */
    /*  The CLKRDY does not work. See Rev. B4 Silicon Errata point. Just wait. */
    NextPacketPtr = RXSTART_INIT;
    /*  Rx start */
    enc28j60Write(ERXSTL, RXSTART_INIT & 0xFF);
    enc28j60Write(ERXSTH, RXSTART_INIT >> 8);
    /*  set receive pointer address */
    enc28j60Write(ERXRDPTL, RXSTART_INIT & 0xFF);
    enc28j60Write(ERXRDPTH, RXSTART_INIT >> 8);
    /*  RX end */
    enc28j60Write(ERXNDL, RXSTOP_INIT & 0xFF);
    enc28j60Write(ERXNDH, RXSTOP_INIT >> 8);
    /*  TX start */
    enc28j60Write(ETXSTL, TXSTART_INIT & 0xFF);
    enc28j60Write(ETXSTH, TXSTART_INIT >> 8);
    /*  TX end */
    enc28j60Write(ETXNDL, TXSTOP_INIT & 0xFF);
    enc28j60Write(ETXNDH, TXSTOP_INIT >> 8);

    /* do bank 1 stuff, packet filter:
     For broadcast packets we allow only ARP packtets
     All other packets should be unicast only for our mac (MAADR) */
    /* for IPv6 without ARP and BC */
    enc28j60Write(ERXFCON, ERXFCON_UCEN | ERXFCON_CRCEN | ERXFCON_PMEN | ERXFCON_MCEN);

    enc28j60Write(EPMM0, 0x3f); /* ARP Pattern Match Filter */
    enc28j60Write(EPMM1, 0x30);
    enc28j60Write(EPMCSL, 0xf9);
    enc28j60Write(EPMCSH, 0xf7);

    /* do bank 2 stuff */
    /* enable MAC receive */
    enc28j60Write(MACON1, MACON1_MARXEN | MACON1_TXPAUS | MACON1_RXPAUS);
    /*   bring MAC out of reset */
    enc28j60Write(MACON2, 0x00);
    /*   enable automatic padding to 60bytes and CRC operations */
    enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, MACON3, MACON3_PADCFG0 | MACON3_TXCRCEN | MACON3_FRMLNEN | MACON3_FULDPX); /* MACON3_HFRMLEN */
    /*   set inter-frame gap (non-back-to-back) */
    enc28j60Write(MAIPGL, 0x12);
    enc28j60Write(MAIPGH, 0x0C);
    /*   set inter-frame gap (back-to-back) */
    enc28j60Write(MABBIPG, 0x15);
    /*   Set the maximum packet size which the controller will accept */
    /*   Do not send packets longer than MAX_FRAMELEN: */
    enc28j60Write(MAMXFLL, MAX_FRAMELEN & 0xFF);
    enc28j60Write(MAMXFLH, MAX_FRAMELEN >> 8);

    /*   do bank 3 stuff */
    /*   write MAC address */
    /*   NOTE: MAC address in ENC28J60 is byte-backward */
    uint8_t addrs[6] = {MAADR5, MAADR4, MAADR3, MAADR2, MAADR1, MAADR0};
    int i;

    for (i = 0; i < sizeof(addrs); i++) {
        enc28j60Write(addrs[i], macaddr[i]);
    }

    enc28j60PhyWrite(PHCON1, 0, PHCON1_PDPXMD);

    /*   no loopback of transmitted frames */
    enc28j60PhyWrite(PHCON2, 0, PHCON2_HDLDIS);

    enc28j60PhyWrite(PHIE, 0, PHIE_PLNKIE | PHIE_PGEIE);

    /*   switch to bank 0 */
    enc28j60SetBank(ECON1);
    /*   enable interrutps */
    enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, EIE, EIE_INTIE);
    enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_PKTIE | EIE_LINKIE | EIE_TXIE | EIE_TXERIE | EIE_RXERIE);
    
    enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_AUTOINC);
    /*   enable packet reception */
    enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN);

}

/**
 *  reset enc28j60
 * @param obj the net work object
 *
 * @return
 *        - status
 */
static int enc28j60Reset(int obj)
{
    int retry = 0;

    ENC28J60_CSH();

    if (obj == RST_ENC28J60_ALL) {
        /* first net hard reset */
        //enc28j60hardreset();
        enc28j60WriteOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET);

        while (!(enc28j60Read(ESTAT) & ESTAT_CLKRDY)) {
            if (retry++ > 0xFFF) {
                return -1;
            }
        }
    } else if (obj == RST_ENC28J60_TX) {
        enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRST);
        enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRST);
    } else if (obj == RST_ENC28J60_RX) {
        enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXRST);
        enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_RXRST);
    }

    return 0;
}

static int enc28j60ResetInit(int obj)
{
    if (enc28j60Reset(RST_ENC28J60_ALL) != 0) {
        printf("enc28j60 reset error\n");
        return -1;
    }

    /*  init enc28j60 module */
    uint8_t macaddr[6];

    csi_eth_mac_get_macaddr(mac_handle, (eth_mac_addr_t *)macaddr);

    enc28j60Init(macaddr);

    //enc28j60_set_interrupt(ENC28J60_ETH_PIN_INT);

    return 0;
}

/**
 *  get rx free space
 * @param void
 *
 * @return
 *        - status 0
 */
static uint16_t enc28j60GetRxFreeSpace(void)
{
    uint16_t free_space;
    uint16_t erxrdpt;
    uint16_t erxwrpt;

    erxrdpt = enc28j60Read(ERXRDPTL);
    erxrdpt |= enc28j60Read(ERXRDPTH) << 8;

    erxwrpt = enc28j60Read(ERXWRPTL);
    erxwrpt |= enc28j60Read(ERXWRPTH) << 8;

    if (erxwrpt > erxrdpt) {
        free_space = (RXSTOP_INIT - RXSTART_INIT) - (erxwrpt - erxrdpt);
    } else if (erxwrpt == erxrdpt) {
        free_space = (RXSTOP_INIT - RXSTART_INIT);
    } else {
        free_space = erxrdpt - erxwrpt - 1;
    }

    return free_space;
}

/**
 * interrupt handle function to post sem for handle
 *
 * @param irqno the irq number of network
 *
 */
uint32_t eth_get_event(int32_t idx)
{
    //eth_mac_priv_t *eth_priv = &s_eth_instance[0];
    uint8_t int_stat, estat;
    uint8_t ptkcnt;
    uint16_t freespace;
    uint16_t status_vec_ptr;
    uint8_t  status_vec[7];
    uint32_t eth_event = 0;

    // clr global interrupt enable bit, then save and clr pending
    // interrupt flags.
    enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, EIE, EIE_INTIE);
    int_stat = enc28j60Read(EIR);
    enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, EIR, int_stat);

    // error flags to be handled first
    if (int_stat & EIR_RXERIF) {
        ptkcnt = enc28j60Read(EPKTCNT);
        freespace = enc28j60GetRxFreeSpace();
        printf("received packet: %d,free space: %d\n", ptkcnt, freespace);
        // if received 255 packets, buffer size is at least 255*64 = 16320
        if ((ptkcnt == 0xff) || (freespace >= 1600)) {
            eth_event |= 0x80;
        }else{
            eth_event |= CSI_ETH_MAC_EVENT_RX_FRAME;
        }
    }

    if (int_stat & EIR_TXERIF) {
        estat = enc28j60Read(ESTAT);

        if ((estat & ESTAT_TXABRT) || (estat & ESTAT_LATECOL)) {
            ETH_LOG_DBG("ESTAT=0x%x\n", estat);
            status_vec_ptr = enc28j60Read(ETXNDL);
            status_vec_ptr |= enc28j60Read(ETXNDH) << 8;
            status_vec_ptr++;
            enc28j60Write(ERDPTL, status_vec_ptr);
            enc28j60Write(ERDPTH, status_vec_ptr >> 8);
            enc28j60ReadBuffer(7, status_vec);
            ETH_LOG_DBG("tx status vector:0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x\n",
                        status_vec[0], status_vec[1], status_vec[2], status_vec[3], status_vec[4], status_vec[5], status_vec[6]);
            eth_event |= 0x80;
        }
    }

    if (int_stat & EIR_LINKIF) {
        eth_event |= CSI_ETH_MAC_EVENT_LINK_CHANGE;
    }

    if (int_stat & EIR_PKTIF) {
        //ptkcnt = enc28j60Read(EPKTCNT); //just for debugging
        //EIR_PKTIF will be cleared if all data is read out
        eth_event |= CSI_ETH_MAC_EVENT_RX_FRAME;
    }

    if (int_stat & EIR_TXIF) {
        eth_event |= CSI_ETH_MAC_EVENT_TX_FRAME;
    }

    return eth_event;
}



csi_drv_version_t csi_eth_phy_get_version(eth_phy_handle_t handle)
{
    csi_drv_version_t version = {0xff, 0xff};
    uint16_t dev_version, dev_version1;

    if (handle == NULL) {
        return version;
    }

    enc28j60Phyregread(PHHID1, 0, &dev_version);
    enc28j60Phyregread(PHHID2, 0, &dev_version1);

    version.api = CSI_ETH_PHY_API_VERSION;
    version.drv = (dev_version << 16) + dev_version1;

    return version;
}

eth_phy_handle_t csi_eth_phy_initialize(csi_eth_phy_read_t fn_read, csi_eth_phy_write_t fn_write)
{
    eth_phy_priv_t *phy_priv;

    if ((fn_read == NULL) || (fn_write == NULL)) {
        return NULL;
    }

    phy_priv = &s_phy_instance[0];

    phy_priv->phy_read  = fn_read ;
    phy_priv->phy_write = fn_write;

    return (eth_mac_handle_t)phy_priv;
}

int32_t csi_eth_phy_uninitialize(eth_phy_handle_t handle)
{
    if (handle == NULL) {
        return -1;
    }

    return 0;
}


int32_t csi_eth_phy_deinit(void)
{
    /*   no loopback of transmitted frames */
    enc28j60PhyWrite(PHCON2, 0, PHCON2_HDLDIS);
    /*  LINK AND ALL PHY Interrupt Enable */
    enc28j60PhyWrite(PHIE, 0, PHIE_PLNKIE | PHIE_PGEIE);
    return 0;
}

int32_t csi_eth_phy_power_control(eth_phy_handle_t handle, eth_power_state_t state)
{
    uint16_t power_control;

    if (handle == NULL) {
        return -1;
    }

    enc28j60Phyregread(PHCON1, 0, &power_control);

    if (state == CSI_ETH_POWER_FULL) {
        power_control &= ~(1 << 11);
    } else if (state == CSI_ETH_POWER_OFF) {
        power_control |= (1 << 11);
    } else if (state == CSI_ETH_POWER_LOW) {

    } else {
        return -1;
    }

    enc28j60PhyWrite(PHCON1, 0, power_control);

    return 0;
}

int32_t csi_eth_phy_set_interface(eth_phy_handle_t handle, uint32_t interface)
{
    if (handle == NULL) {
        return -1;
    }

    return 0;
}

eth_link_info_t csi_eth_phy_get_linkinfo(eth_phy_handle_t handle)
{
    eth_link_info_t net_link_info = {0};

    if (handle == NULL) {
        return net_link_info;
    }

    net_link_info.duplex = 1;
    net_link_info.speed = 1;

    return net_link_info;
}

int32_t csi_eth_phy_set_mode(eth_phy_handle_t handle, uint32_t mode)
{
    uint32_t phy_mode = 0;

    if (handle == NULL) {
        return -1;
    }

    eth_phy_priv_t *phy_priv = (eth_phy_priv_t *)handle;

    phy_priv->link_info.speed    = (mode & 0x03);
    phy_priv->link_info.duplex   = (mode & 0x04);
    phy_priv->link_info.Loopback = (mode & 0x05);

    if (phy_priv->link_info.duplex) {
        phy_mode |= PHCON1_PDPXMD;
    } else {
        phy_mode &= ~(PHCON1_PDPXMD);
    }

    if (phy_priv->link_info.Loopback) {
        phy_mode |= PHCON1_PLOOPBK;
    } else {
        phy_mode &= ~(PHCON1_PLOOPBK);
    }

    enc28j60PhyWrite(PHCON1, 0, phy_mode);

    return 0;
}

eth_link_state_t csi_eth_phy_get_linkstate(eth_phy_handle_t handle)
{
    uint16_t phstat1;
    uint16_t phstat2;
    eth_link_state_t state;

    if (handle == NULL) {
        return -1;
    }

    uint8_t status = enc28j60Read(EIR);

    if (status & EIR_LINKIF) {
        /*  as tested, need to read twice */
        enc28j60Phyregread(PHSTAT1, 0, &phstat1);
        enc28j60Phyregread(PHSTAT1, 0, &phstat2);

        phstat1 |= phstat2;

        if (phstat1 & 0x0004) {
            state = ETH_LINK_UP;
        } else {
            state = ETH_LINK_DOWN;
        }

        /*  resets to "0" when read */
        enc28j60Phyregread(PHIR, 0, NULL);

        return state;
    }

    return -1;
}

eth_mac_handle_t csi_eth_mac_initialize(int32_t idx, eth_event_cb_t cb_event)
{
    eth_mac_priv_t *eth_priv;
    static int eth_mac_init = 0;
    int ret = -1;

    eth_priv = &s_eth_instance[0];
    eth_priv->cb_event = cb_event;

    ETH_LOG_DBG("eth int cb %p\n", cb_event);

    if (eth_mac_init == 0) {
        //init spi get spi_handle
        g_net_spi_hd = csi_spi_initialize(ENC28J60_ETH_SPI_IDX, (spi_event_cb_t)enc28j60_spi_transfer_callback);

        //spi_handle success goto config spi
        if (g_net_spi_hd != NULL) {

            ret = csi_spi_config(g_net_spi_hd, SYSTEM_CLOCK / 10, SPI_MODE_MASTER, SPI_FORMAT_CPOL0_CPHA0,
                                 SPI_ORDER_MSB2LSB, SPI_SS_MASTER_SW, 8);

            pgpio_pin_handle1 = csi_gpio_pin_initialize(ENC28J60_ETH_SPI_CS, NULL);
            csi_gpio_pin_config_mode(pgpio_pin_handle1, GPIO_MODE_PULLNONE);
            csi_gpio_pin_config_direction(pgpio_pin_handle1, GPIO_DIRECTION_OUTPUT);

        }

        eth_mac_init = 1;
    }

    if ((ret == 0) || (eth_mac_init == 1)) {

        if (enc28j60ResetInit(RST_ENC28J60_ALL) == 0) {
            return (eth_mac_handle_t)eth_priv;
        }
    }

    return NULL;
}

int32_t csi_eth_mac_uninitialize(eth_mac_handle_t handle)
{
    if (handle == NULL) {
        return -1;
    }

    return 0;
}

csi_drv_version_t csi_eth_mac_get_version(eth_mac_handle_t handle)
{
    csi_drv_version_t mac_version = {0xff, 0xff};

    if (handle == NULL) {
        return mac_version;
    }

    mac_version.api = CSI_ETH_PHY_API_VERSION;
    mac_version.drv = enc28j60Read(EREVID);

    return mac_version;
}

/**
To maximize power savings:
1. Turn off packet reception by clearing ECON1.RXEN.
2. Wait for any in-progress packets to finish being received by polling ESTAT.RXBUSY.
This bit should be clear before proceeding.
3. Wait for any current transmissions to end by confirming ECON1.TXRTS is clear.
4. Set ECON2.VRPS (if not already set).
5. Enter Sleep by setting ECON2.PWRSV. All MAC, MII and PHY registers become
inaccessible as a result. Setting PWRSV also clears ESTAT.CLKRDY automatically.
*/
int32_t csi_eth_mac_power_control(eth_mac_handle_t handle, eth_power_state_t state)
{
    //uint8_t reg;

    if (handle == NULL) {
        return -1;
    }

    if (state == CSI_ETH_POWER_FULL) {
        enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON2, ECON2_PWRSV);
        enc28j60ResetInit(RST_ENC28J60_ALL);
    } else if (state == CSI_ETH_POWER_OFF) {
        int retry = 0;
        enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_RXEN);

        while (enc28j60Read(ESTAT) & ESTAT_RXBUSY) {
            if (retry++ > 0xFFF) {
                printf("send err\n");
                return -1;
            }
        }

        retry = 0;

        while (enc28j60Read(ECON1) & ECON1_TXRTS) {
            if (retry++ > 0xFFF) {
                printf("send err\n");
                return -1;
            }
        }

        enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_VRPS);
        enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PWRSV);
    } else if (state == CSI_ETH_POWER_LOW) {

    } else {
        return -1;
    }

    return 0;
}

int32_t csi_eth_mac_get_macaddr(eth_mac_handle_t handle, eth_mac_addr_t *mac)
{
    if ((handle == NULL) || (mac == NULL)) {
        return -1;
    }

    memcpy(mac, g_hw_addr, NET_HWADDR_LEN);
    return 0;
}

int32_t csi_eth_mac_set_macaddr(eth_mac_handle_t handle, const eth_mac_addr_t *mac)
{
    if ((mac == NULL)) {
        return -1;
    }

    memcpy(g_hw_addr, mac, NET_HWADDR_LEN);
    ETH_LOG_DBG("csiMAC: %02x:%02x:%02x:%02x:%02x:%02x\n", g_hw_addr[0], g_hw_addr[1], g_hw_addr[2],
                g_hw_addr[3], g_hw_addr[4], g_hw_addr[5]);

    //enc28j60Setmacaddr(g_hw_addr);
    return 0;
}

int32_t csi_eth_mac_set_addrfilter(eth_mac_handle_t handle, const eth_mac_addr_t *addr, uint32_t num_addr)
{
    if ((handle == NULL) || (addr == NULL)) {
        return -1;
    }

    return 0;
}

int32_t csi_eth_mac_get_rx_framesize(eth_mac_handle_t handle)
{
    if (handle == NULL) {
        return -1;
    }

    return 0;
}

int32_t csi_eth_mac_get_rx_frametime(eth_mac_handle_t handle, eth_mac_time_t *time)
{
    if ((handle == NULL)) {
        return -1;
    }

    return 0;
}

int32_t csi_eth_mac_get_tx_frametime(eth_mac_handle_t handle, eth_mac_time_t *time)
{
    if (handle == NULL) {
        return -1;
    }

    return 0;
}

int32_t csi_eth_mac_control(eth_mac_handle_t handle, uint32_t control, uint32_t arg)
{
    uint8_t  reg_data = 0;

    if (handle == NULL) {
        return -1;
    }

    if (control == CSI_ETH_MAC_CONFIGURE) {
        if ((arg & CSI_ETH_MAC_LOOPBACK) == CSI_ETH_MAC_LOOPBACK) {
            enc28j60PhyWrite(PHCON1, 0, PHCON1_PLOOPBK);

            reg_data = enc28j60Read(MACON1);
            enc28j60Write(MACON1, reg_data | MACON1_LOOPBK);
        }
    } else if (control == CSI_ETH_MAC_CONTROL_RX) {
        if (arg) {
            enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE);

        } else {
            enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, EIE, EIE_INTIE);
        }

        return 0;
    }

    return 0;
}

/**
  \brief       Send Ethernet frame.
  \param[in]   handle  ethernet handle
  \param[in]   frame  Pointer to frame buffer with data to send
  \param[in]   len    Frame buffer length in bytes
  \param[in]   flags  Frame transmit flags (see CSI_ETH_MAC_TX_FRAME_...)
  \return      error code
*/
int32_t csi_eth_mac_send_frame(eth_mac_handle_t handle, const uint8_t *frame, uint32_t len, uint32_t flags)
{
    int retry = 0;

    if (len > MAX_FRAMELEN) {
        printf("TX %d too large\n", len);
        return 0;
    }

    while (enc28j60Read(ECON1) & ECON1_TXRTS) {
        if (retry++ > 0xFFF) {
            printf("send err\n");
            return -1;
        }
    }

    /*   Set the write pointer to start of transmit buffer area */
    enc28j60Write(EWRPTL, TXSTART_INIT & 0xFF);
    enc28j60Write(EWRPTH, TXSTART_INIT >> 8);

    /*   Set the TXND pointer to correspond to the packet size given */
    /*   Status vector will be written at ETXND+1. */
    enc28j60Write(ETXNDL, (TXSTART_INIT + len) & 0xFF);
    enc28j60Write(ETXNDH, (TXSTART_INIT + len) >> 8);

    /*   write per-packet control byte (0x00 means use macon3 settings) */
    enc28j60WriteOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00);

    /*   copy the packet into the transmit buffer */
    enc28j60WriteBuffer(len, (uint8_t *)frame);

    /*   send the contents of the transmit buffer onto the network */
    enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS);

    /*   Reset the transmit logic problem. See Rev. B4 Silicon Errata point 12. */
    if ((enc28j60Read(EIR) & EIR_TXERIF)) {
        enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRTS);
    }

    return len;
}

/**
  \brief       Read data of received Ethernet frame.
  \param[in]   handle  ethernet handle
  \param[in]   frame  Pointer to frame buffer for data to read into
  \param[in]   len    Frame buffer length in bytes
  \return      number of data bytes read or execution status
                 - value >= 0: number of data bytes read
                 - value < 0: error occurred, value is execution status as defined with execution_status
*/
int32_t csi_eth_mac_read_frame(eth_mac_handle_t handle, uint8_t *frame, uint32_t len)
{
    uint16_t rlen;
    uint16_t rxstat;

    if (handle == NULL) {
        return -1;
    }

    /*   check if a packet has been received and buffered
    if( !(enc28j60Read(EIR) & EIR_PKTIF) ){
    The above does not work. See Rev. B4 Silicon Errata point 6.  */
    if (enc28j60Read(EPKTCNT) == 0) {
        return (0);
    }

    /*   Set the read pointer to the start of the received packet  */
    enc28j60Write(ERDPTL, (NextPacketPtr));
    enc28j60Write(ERDPTH, (NextPacketPtr) >> 8);

    /*   read the next packet pointer */
    NextPacketPtr  = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0);
    NextPacketPtr |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0) << 8;

    /*   read the packet length (see datasheet page 43) */
    rlen  = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0);
    rlen |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0) << 8;
    rlen -= 4; /*  remove the CRC count */

    /*   limit retrieve length */
    if (rlen > MAX_FRAMELEN - 1) {
        printf("rx len %d\n", rlen);
        rlen = MAX_FRAMELEN - 1;
        return -1;
    }

    /*   read the receive status (see datasheet page 43) */
    rxstat = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0);
    rxstat |= (uint16_t)enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0) << 8;

    /*   check CRC and symbol errors (see datasheet page 44, table 7-3): */
    /*   The ERXFCON.CRCEN is set by default. Normally we should not */
    /*   need to check this. */
    if ((rxstat & 0x80) == 0) {
        printf("rx stat 0x%x\n", rxstat);
        rlen = 0; // invalid
        return -1;
    }

    /*   copy the packet from the receive buffer */
    enc28j60ReadBuffer(rlen, frame);

    /*   Move the RX read pointer to the start of the next received packet */
    /*   This frees the memory we just read out */
    enc28j60Write(ERXRDPTL, (NextPacketPtr));
    enc28j60Write(ERXRDPTH, (NextPacketPtr) >> 8);

    /*   decrement the packet counter indicate we are done with this packet */
    enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_AUTOINC | ECON2_PKTDEC);

    len = rlen;
    return rlen;

}

eth_capabilities_t csi_eth_mac_get_capabilities(int32_t idx)
{
    eth_capabilities_t  capab = {0};

    capab.mac_address = 1;
    capab.event_rx_frame = 1;

    return capab;
}

void csi_eth_mac_signal_event(eth_mac_handle_t handle, uint32_t event)
{
    if (handle == NULL) {
        return;
    }

}

int32_t csi_eth_mac_control_time(eth_mac_handle_t handle, uint32_t control, eth_mac_time_t *time)
{
    if (handle == NULL) {
        return -1;
    }

    return 0;
}

int32_t csi_eth_mac_phy_read(eth_mac_handle_t handle, uint8_t phy_addr, uint8_t reg_addr, uint16_t *data)
{
    if ((handle == NULL) || (data == NULL)) {
        return -1;
    }

    return 0;
}

int32_t csi_eth_mac_phy_write(eth_mac_handle_t handle, uint8_t phy_addr, uint8_t reg_addr, uint16_t data)
{
    if ((handle == NULL)) {
        return -1;
    }

    return 0;
}

int32_t csi_eth_mac_add_framefilter(eth_mac_handle_t handle, const eth_frame_filter_t *filter)
{
    if ((handle == NULL) || (filter == NULL)) {
        return -1;
    }

    return 0;
}

int32_t csi_eth_mac_remove_framefilter(eth_mac_handle_t handle, uint32_t filter_id)
{
    if (handle == NULL) {
        return -1;
    }

    return 0;
}

int32_t csi_eth_mac_en_framefilter(eth_mac_handle_t handle, uint32_t filter_id, bool en)
{
    if (handle == NULL) {
        return -1;
    }

    enc28j60Write(ERXFCON, en);
    enc28j60Write(EPMM0, en); /* ARP Pattern Match Filter */
    enc28j60Write(EPMM1, en);
    enc28j60Write(EPMCSL, en);
    enc28j60Write(EPMCSH, en);

    return 0;
}

int32_t csi_eth_mac_get_framefilter(eth_mac_handle_t handle, eth_frame_filter_list_t *list, uint32_t *count_out, uint32_t max_count)
{
    if ((handle == NULL) || (list == NULL)) {
        return -1;
    }

    return 0;
}

#ifdef CONFIG_ETH_ENC28J60_USE_PBUF

/**
  \brief       Begin to Send Ethernet frame.
  \param[in]   handle  ethernet handle
  \param[in]   len    Frame buffer length in bytes
  \return      error code
*/
int32_t csi_eth_mac_ex_send_frame_begin(eth_mac_handle_t handle, uint32_t len)
{
    int retry = 0;

    if (len > MAX_FRAMELEN) {
        printf("TX %d too\n", len);
        return 0;
    }

    /* sometimes, int is missing, force to pull up interrupt line
    and enable interrupt to check if any pending event */
    //enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, EIE, EIE_INTIE);
    //enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE);

    while (enc28j60Read(ECON1) & ECON1_TXRTS) {
        if (retry++ > 0xFFF) {
            printf("send err\n");
            return -1;
        }
    }

    /*   Set the write pointer to start of transmit buffer area */
    enc28j60Write(EWRPTL, TXSTART_INIT & 0xFF);
    enc28j60Write(EWRPTH, TXSTART_INIT >> 8);

    /*   Set the TXND pointer to correspond to the packet size given */
    /*   Status vector will be written at ETXND+1. */
    enc28j60Write(ETXNDL, (TXSTART_INIT + len) & 0xFF);
    enc28j60Write(ETXNDH, (TXSTART_INIT + len) >> 8);

    /*   write per-packet control byte (0x00 means use macon3 settings) */
    enc28j60WriteOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00);

    return 0;
}

/**
 *   send a packet data
 * @param address the packet data length
 *
 * @return
 *        - sent data length
 */
int32_t csi_eth_mac_ex_send_frame(eth_mac_handle_t handle, const uint8_t *frame, uint32_t len, uint32_t flags)
{
    /*   copy the packet into the transmit buffer */
    enc28j60WriteBuffer(len, (uint8_t *)frame);

    return len;
}

/**
  \brief       End Send Ethernet frame.
  \param[in]   handle  ethernet handle
  \return      error code
*/
int32_t csi_eth_mac_ex_send_frame_end(eth_mac_handle_t handle)
{
    /*   send the contents of the transmit buffer onto the network */
    enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS);

    /*   Reset the transmit logic problem. See Rev. B4 Silicon Errata point 12. */
    if ((enc28j60Read(EIR) & EIR_TXERIF)) {
        enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRTS);
    }

    csi_kernel_delay_ms(10);
    
    return 0;
}

/**
  \brief       Begin to Read data of received Ethernet frame.
  \param[in]   handle  ethernet handle
  \param[in]   len    Frame buffer length in bytes
  \return      >0 data byte in hardware buffer
               ==0 no data
               < 0 error
*/
int32_t csi_eth_mac_ex_read_frame_begin(eth_mac_handle_t handle)
{
    uint16_t len;
    uint16_t rxstat;

    if (handle == NULL) {
        return -1;
    }

    /*   check if a packet has been received and buffered
    if( !(enc28j60Read(EIR) & EIR_PKTIF) ){
    The above does not work. See Rev. B4 Silicon Errata point 6.  */
    if (enc28j60Read(EPKTCNT) == 0) {
        return (0);
    }

    /*   Set the read pointer to the start of the received packet  */
    enc28j60Write(ERDPTL, (NextPacketPtr));
    enc28j60Write(ERDPTH, (NextPacketPtr) >> 8);

    /*   read the next packet pointer */
    NextPacketPtr  = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0);
    NextPacketPtr |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0) << 8;

    /*   read the packet length (see datasheet page 43) */
    len  = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0);
    len |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0) << 8;
    len -= 4; /*  remove the CRC count */

    /*   limit retrieve length */
    if (len > MAX_FRAMELEN - 1) {
        printf("rx len %d\n", len);
        len = MAX_FRAMELEN - 1;
        return -1;
    }

    /*   read the receive status (see datasheet page 43) */
    rxstat = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0);
    rxstat |= (uint16_t)enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0) << 8;

    /*   check CRC and symbol errors (see datasheet page 44, table 7-3): */
    /*   The ERXFCON.CRCEN is set by default. Normally we should not */
    /*   need to check this. */
    if ((rxstat & 0x80) == 0) {
        printf("rx stat 0x%x\n", rxstat);
        len = 0; // invalid
        return -1;
    }

    return len;

}

int32_t csi_eth_mac_ex_read_frame(eth_mac_handle_t handle, uint8_t *frame, uint32_t len)
{
    /*   copy the packet from the receive buffer */
    enc28j60ReadBuffer(len, frame);

    return len;
}

/**
  \brief       Begin to Read data of received Ethernet frame.
  \param[in]   handle  ethernet handle
  \return      error code
*/
int32_t csi_eth_mac_ex_read_frame_end(eth_mac_handle_t handle)
{
    /*   Move the RX read pointer to the start of the next received packet */
    /*   This frees the memory we just read out */
    enc28j60Write(ERXRDPTL, (NextPacketPtr));
    enc28j60Write(ERXRDPTH, (NextPacketPtr) >> 8);

    /*   decrement the packet counter indicate we are done with this packet */
    enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON2, /*ECON2_AUTOINC | */ECON2_PKTDEC);

    return 0;
}

#endif/* CONFIG_ETH_ENC28J60_USE_PBUF */

#endif/* CONFIG_ETH_ENC28J60 */
